;(function(win){
        var store = {},
                doc = win.document,
                localStorageName = 'localStorage',
                namespace = '__storejs__',
                storage;

        store.disabled = false;
        store.set = function(key, value) {};
        store.get = function(key) {};
        store.remove = function(key) {};
        store.clear = function() {};
        store.transact = function(key, defaultVal, transactionFn) {
                var val = store.get(key)
                if (transactionFn == null) {
                        transactionFn = defaultVal
                        defaultVal = null
                }
                if (typeof val == 'undefined') { val = defaultVal || {} }
                transactionFn(val)
                store.set(key, val)
        };
        store.getAll = function() {};

        store.serialize = function(value) {
                return JSON.stringify(value);
        };
        store.deserialize = function(value) {
                if (typeof value != 'string') { return undefined };
                try { 
                        return JSON.parse(value);
                }catch(e) { 
                        return value || undefined;
                }
        };

        // Functions to encapsulate questionable FireFox 3.6.13 behavior
        // when about.config::dom.storage.enabled === false
        // See https://github.com/marcuswestin/store.js/issues#issue/13
        function isLocalStorageNameSupported() {
                try { 
                        return (localStorageName in win && win[localStorageName]);
                }catch(err) { 
                        return false;
                }
        }
        if (isLocalStorageNameSupported()) {
                storage = win[localStorageName];
                store.set = function(key, val) {
                        if (val === undefined) { 
                                return store.remove(key);
                        }
                        storage.setItem(key, store.serialize(val));
                        return val;
                }
                store.get = function(key) { return store.deserialize(storage.getItem(key)) };
                store.remove = function(key) { 
                        storage.removeItem(key);
                };
                store.clear = function() { 
                        storage.clear();
                };
                store.getAll = function() {
                        var ret = {};
                        for (var i=0; i<storage.length; ++i) {
                                var key = storage.key(i);
                                ret[key] = store.get(key);
                        }
                        return ret;
                };
        } else if (doc.documentElement.addBehavior) {
                var storageOwner,
                        storageContainer;
                try {
                        storageContainer = new ActiveXObject('htmlfile');
                        storageContainer.open();
                        storageContainer.write('<script>document.w=window;</script><iframe src="/favicon.ico"></iframe>');
                        storageContainer.close();
                        storageOwner = storageContainer.w.frames[0].document;
                        storage = storageOwner.createElement('div');
                } catch(e) {
                        // somehow ActiveXObject instantiation failed (perhaps some special
                        // security settings or otherwse), fall back to per-path storage
                        storage = doc.createElement('div');
                        storageOwner = doc.body;
                }
                function withIEStorage(storeFunction) {
                        return function() {
                                var args = Array.prototype.slice.call(arguments, 0);
                                args.unshift(storage);
                                // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
                                // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
                                storageOwner.appendChild(storage);
                                storage.addBehavior('#default#userData');
                                storage.load(localStorageName);
                                var result = storeFunction.apply(store, args);
                                storageOwner.removeChild(storage);
                                return result;
                        }
                }
                // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
                var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
                function ieKeyFix(key) {
                        return key.replace(forbiddenCharsRegex, '___');
                }
                store.set = withIEStorage(function(storage, key, val) {
                        key = ieKeyFix(key);
                        if (val === undefined) { 
                                return store.remove(key); 
                        }
                        storage.setAttribute(key, store.serialize(val));
                        storage.save(localStorageName);
                        return val;
                });
                store.get = withIEStorage(function(storage, key) {
                        key = ieKeyFix(key);
                        return store.deserialize(storage.getAttribute(key));
                });
                store.remove = withIEStorage(function(storage, key) {
                        key = ieKeyFix(key);
                        storage.removeAttribute(key);
                        storage.save(localStorageName);
                });
                store.clear = withIEStorage(function(storage) {
                        var attributes = storage.XMLDocument.documentElement.attributes;
                        storage.load(localStorageName);
                        for (var i=0, attr; attr=attributes[i]; i++) {
                                storage.removeAttribute(attr.name);
                        }
                        storage.save(localStorageName);
                });
                store.getAll = withIEStorage(function(storage) {
                        var attributes = storage.XMLDocument.documentElement.attributes;
                        var ret = {};
                        for (var i=0, attr; attr=attributes[i]; ++i) {
                                var key = ieKeyFix(attr.name);
                                ret[attr.name] = store.deserialize(storage.getAttribute(key));
                        }
                        return ret;
                })
        }

        try {
                store.set(namespace, namespace);
                if (store.get(namespace) != namespace) { 
                        store.disabled = true; 
                }
                store.remove(namespace);
        } catch(e) {
                store.disabled = true;
        }
        store.enabled = !store.disabled;
        if (typeof module != 'undefined' && module.exports) { 
                module.exports = store; 
        }else if (typeof define === 'function' && define.amd) { 
                define(store);
        }else { 
                win.store = store;
        }
})(this.window || global);